package com.tamartech.greendb.loader;

import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang.time.DateUtils;
import org.apache.log4j.Logger;

import com.tamartech.greendb.dao.AddressDAO;
import com.tamartech.greendb.dao.ElectoralRollEntryDAO;
import com.tamartech.greendb.dao.PersonDAO;
import com.tamartech.greendb.dao.PersonMatchDAO;
import com.tamartech.greendb.data.Address;
import com.tamartech.greendb.data.ElectoralRoll;
import com.tamartech.greendb.data.ElectoralRollEntry;
import com.tamartech.greendb.data.Person;
import com.tamartech.greendb.data.PersonMatch;

public class PersonMatcher
{
    private static Logger log = Logger.getLogger (PersonMatcher.class);

    private Person thePerson;
    private Address theAddress;

    private boolean newPerson = false;
    private boolean newAddress = false;

    private static Date oneYearAgo;

    static
    {
        oneYearAgo = DateUtils.addYears (new Date(), -1);
        // ?? nasty frig for initial loading
//        oneYearAgo = DateUtils.addYears (new Date(), -2);
    }
    public PersonMatcher(Person person, Address address, ElectoralRoll roll) throws Exception
    {
        doIt(person, address, roll);
    }

    public Person getPerson()
    {
        return thePerson;
    }

    public Address getAddress()
    {
        return theAddress;
    }

    private void doIt(Person person, Address address, ElectoralRoll roll) throws Exception
    {
        AddressDAO addressDAO = new AddressDAO();
        PersonDAO personDAO = new PersonDAO();
        PersonMatchDAO personMatchDAO = new PersonMatchDAO();
        ElectoralRollEntryDAO electoralRollEntryDAO = new ElectoralRollEntryDAO();

        // for the moment
        thePerson = person;
        theAddress = address;

        // we will do a global person search, unless we get a match for this
        // person at the same address on the latest electoral roll
        boolean needPersonSearch = true;

        // if this address already exists in DB, use it. Otherwise save it
        Address foundAddress = addressDAO.find(address);

        if (foundAddress == null)
        {
            newAddress = true;
            address.setFirstSeenRoll(roll);
            theAddress = addressDAO.save(address);
        }
        else
        {
            theAddress = foundAddress;

            List<ElectoralRollEntry> entries = electoralRollEntryDAO.findByAddress(theAddress);

            Collections.sort(entries, new Comparator<ElectoralRollEntry>()
            {
                @Override
                public int compare(ElectoralRollEntry l, ElectoralRollEntry r)
                {
                    // reverse the comparison to get descending order
                    return r.getElectoralRoll().compareTo(l.getElectoralRoll());
                }
            });

            // traverse the list and try and match the person
            for (ElectoralRollEntry e : entries)
            {
                /* Originally did this by checking whether the person was at the same address on the latest
                 * electoral roll, but now I have split rolls up by ward, I could miss out if the address changes
                 * ward.
                 * For the moment I am going to check whether a person with this name was at the same address
                 * less than a year ago.
                 * Could take the latest roll for every ward and check with each, but that is starting to get
                 * complicated.
                 * Even if I miss an exact match here, we should still record a possible match in the
                 * PersonMatch table below
                 *
                 */

                if (oneYearAgo.compareTo(e.getElectoralRoll().getDate()) > 0)
                    break;

//                if (person.equals(e.getPerson()))
                if (person.equalsIgnoreCase(e.getPerson()))
                {
                    // check that this is for the latest electoral roll
                    //if (latestRoll.equals(e.getElectoralRoll()))

                    thePerson = e.getPerson();
                    needPersonSearch = false;

                    log.info("Matched person: " + thePerson + " --- " + theAddress);

                    // we have a match - no point in searching this list further
                    break;
                }
            }
        }

        if (needPersonSearch)
        {
            // try and find any matching names
            List<Person> matches = personDAO.findByName(person);

            List<PersonMatch> matchList = new LinkedList<PersonMatch>();

            // create person match objects
            for (Person match : matches)
            {
                PersonMatch pm = new PersonMatch();

                pm.setPerson1(person);
                pm.setPerson2(match);

                matchList.add(pm);
            }

            // log and save the new person
            newPerson = true;
            person.setFirstSeenRoll(roll);
            thePerson = personDAO.save(person);

            // save the person match objects
            for (PersonMatch pm : matchList)
            {
                personMatchDAO.save(pm);
            }
        }

    }

    public boolean isNewPerson()
    {
        return newPerson;
    }

    public boolean isNewAddress()
    {
        return newAddress;
    }
}
